#include <poll.h>

#include "rpc/net/socket.h"
#include "rpc/net/connector.h"
#include "rpc/net/socket_opt.h"
#include "include/event_manager.h"

namespace rpc {
Socket* Connector::Connect(const std::string& server_ip, uint16 server_port,
                           uint32 timeout, const std::string& client_ip) {

  Addr cli_addr(client_ip, 0);
  scoped_ptr<Socket> s(new Socket);
  if (sock::Bind(s.get(), cli_addr)) {
    return NULL;
  }
  s->SetBlock(false);

  Addr serv_addr(server_ip, server_port);
  int32 ret = ::connect(s->Fd(), (struct sockaddr*) &serv_addr.addr,
                        sizeof(serv_addr.addr));
  if (ret == 0) {
    return s.release();
  }

  while (timeout > 0) {
    struct timeval before = { 0 }, after = { 0 };
    ::gettimeofday(&before, NULL);
    if (WaitForConnected(s.get(), timeout)) {
      return s.release();
    }
    ::gettimeofday(&after, NULL);
    timeout -= after.tv_sec - before.tv_sec;
  }

  return NULL;
}

bool Connector::WaitForConnected(Socket* sock, int32 timeout) {
  CHECK(sock != NULL || timeout >= 0);

  struct pollfd pfd[1];
  pfd[0].fd = sock->Fd();
  pfd[0].events = POLLOUT;
  pfd[0].revents = 0;

  int32 ret = ::poll((struct pollfd*) &pfd, 1, timeout);
  if (ret != 1 || !(pfd[1].revents & POLLOUT)) {
    return false;
  }

  uint32 val, len = sizeof(uint32);
  if (!sock::GetSockOpt(sock, SOL_SOCKET, SO_ERROR, &val, &len)) {
    return false;
  }

  return val == 0;
}

}
